// Copyright 2011 Google Inc. All Rights Reserved.

package naclrun

import (
	"fmt"
	"os"
	"path"
)

const (
	MaxPathLength = 2048
)

// Proxy defines all possible NaCl module requests
// for potentially unsafe POSIX functions.
type Proxy interface {
	FS
	// Nothing else yet
}

// FS defines file system operations for NaCl module.
type FS interface {
	Open(filename string, flags int, mode uint32) (f *os.File, err os.Error)
}

type virtualFS struct {
	r      Resolver
	rdonly bool
}

// Open implements FS.Open.
func (fs *virtualFS) Open(filename string, flags int, mode uint32) (f *os.File, err os.Error) {
	realFileName, ok := fs.r.ResolveFile(filename)
	if !ok {
		return nil, fmt.Errorf("virtual_file_system: can't resolve virtual filename: %s", filename)
	}
	if fs.rdonly && flags != os.O_RDONLY {
		return nil, os.EACCES
	}
	return os.OpenFile(realFileName, flags, mode)
}

// NewVirtualFS returns a new syscall proxy
// on top of specified resolver.
// If true, rdonly parameter marks the whole file system as read-only.
func NewVirtualFS(r Resolver, rdonly bool) FS {
	return &virtualFS{r: r, rdonly: rdonly}
}

// CompositeFS stacks several proxies on top of each other.
type CompositeFS struct {
	fs []FS
}

// NewCompositeFS returns a new CompositeFS with the given file systems.
func NewCompositeFS(fs ...FS) *CompositeFS {
	return &CompositeFS{fs: fs}
}

// Add adds the new portion of file systems.
func (fs *CompositeFS) Add(newFs ...FS) {
	fs.fs = append(fs.fs, newFs...)
}

// Open implements FS.Open.
func (fs *CompositeFS) Open(filename string, flags int, mode uint32) (f *os.File, err os.Error) {
	if len(filename) > MaxPathLength {
		return nil, fmt.Errorf("virtual_file_system: filename too long: %d bytes", len(filename))
	}
	filename = path.Clean(filename)

	if len(fs.fs) == 0 {
		return nil, fmt.Errorf("File not found: %s", filename)
	}
	for _, cur := range fs.fs {
		f, err = cur.Open(filename, flags, mode)
		if err == nil {
			break
		}
	}
	return
}
